package com.ccp.dev.system.service;

import com.ccp.dev.core.basic.api.ISysJobLogService;
import com.ccp.dev.core.basic.scheduler.ParameterObj;
import com.ccp.dev.core.basic.scheduler.PlanObject;
import com.ccp.dev.core.basic.scheduler.TriggerObject;
import com.ccp.dev.core.basic.web.query.QueryFilter;
import com.ccp.dev.core.util.AppUtil;
import com.ccp.dev.core.util.StringPool;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.core.util.TimeUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.text.StringEscapeUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.*;

/**
 * 定时任务service
 * @author nd
 */
@Service
public class SchedulerService {

    private static final String SCHEDULER_GROUP = "group1";

    @Autowired
    private Scheduler scheduler;

    private static HashMap<String, String> mapWeek;

    static {
        /*
        "MON,TUE,WED,THU,FRI,SAT,SUN"
         */
        mapWeek = new HashMap<String, String>(7);
        mapWeek.put("MON", "星期一");
        mapWeek.put("TUE", "星期二");
        mapWeek.put("WED", "星期三");
        mapWeek.put("THU", "星期四");
        mapWeek.put("FRI", "星期五");
        mapWeek.put("SAT", "星期六");
        mapWeek.put("SUN", "星期日");

    }


    /**
     * 查询任务列表
     *
     * @param queryFilter 查询条件
     * @return List<JobDetail>
     * @throws SchedulerException 异常
     */
    public List<JobDetail> queryJobList(QueryFilter queryFilter) throws SchedulerException {
        if (scheduler == null) {
            return new ArrayList<JobDetail>();
        }
        List<JobDetail> list = new ArrayList<JobDetail>();
        GroupMatcher<JobKey> matcher = GroupMatcher.groupEquals(SCHEDULER_GROUP);
        Set<JobKey> set = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : set) {
            JobDetail detail = scheduler.getJobDetail(jobKey);
            list.add(detail);
        }
        return list;
    }

    /**
     * 新增或修改
     *
     * @param jobName       任务名称
     * @param className     任务类
     * @param parameterJson 参数
     * @param description   任务描述
     */
    public boolean saveJob(String jobName, String className, String parameterJson, String description) throws SchedulerException, ClassNotFoundException {
        if (scheduler == null) {
            return false;
        }
        Class<Job> cls = (Class<Job>) Class.forName(className);
        JobBuilder jb = JobBuilder.newJob(cls);
        jb.withIdentity(jobName, SCHEDULER_GROUP);
        if (StringUtil.isNotEmpty(parameterJson)) {
            setJobMap(parameterJson, jb);
        }
        jb.storeDurably();
        jb.withDescription(description);
        JobDetail jobDetail = jb.build();
        scheduler.addJob(jobDetail, true);
        return true;
    }

    /**
     * 添加任务
     *
     * @param jobName      任务名称
     * @param className    类的全路径
     * @param parameterMap 参数
     * @param description  任务描述
     * @throws SchedulerException
     * @throws ClassNotFoundException
     */
    private String addJob(String jobName, String className, Map parameterMap, String description) throws SchedulerException {
        if (scheduler == null) {
            return "scheduler 没有配置!";
        }
        String resultMsg = null;
        boolean isJobExist = isJobExists(jobName);
        if (isJobExist) {
            resultMsg = "任务已存在";
            return resultMsg;
        }
        Class<Job> cls;
        try {
            cls = (Class<Job>) Class.forName(className);
        } catch (ClassNotFoundException e) {
            resultMsg = "指定的任务类不存在，或者没有实现JOB接口";
            return resultMsg;
        }

        try {
            resultMsg = addJob(jobName, cls, parameterMap, description);
            return resultMsg;
        } catch (Exception e) {
            resultMsg = e.getMessage();
            return resultMsg;
        }
    }

    /**
     * @param jobName 任务名
     * @param cls 任务类
     * @param parameterMap 参数
     * @param description 描述
     * @return resultMsg
     */
    private String addJob(String jobName, Class cls, Map parameterMap, String description) throws SchedulerException, ClassNotFoundException {
        if (scheduler == null) {
            return "scheduler 没有配置!";
        }

        String resultMsg = null;

        JobBuilder jb = JobBuilder.newJob(cls);
        jb.withIdentity(jobName, SCHEDULER_GROUP);
        if (parameterMap != null) {
            JobDataMap map = new JobDataMap();
            map.putAll(parameterMap);
            jb.usingJobData(map);
        }
        jb.storeDurably();
        jb.withDescription(description);
        JobDetail jobDetail = jb.build();
        scheduler.addJob(jobDetail, true);
        resultMsg = "添加任务成功!";
        return resultMsg;
    }

    /**
     * 取得任务是否存在
     *
     * @param jobName 任务名
     * @return boolean
     * @throws SchedulerException 异常
     */
    public boolean isJobExists(String jobName) throws SchedulerException {
        if (scheduler == null) {
            return false;
        }
        JobKey key = new JobKey(jobName, SCHEDULER_GROUP);
        return scheduler.checkExists(key);
    }
    /**
     * 根据任务名称获取触发器
     *
     * @param jobName 任务名
     * @return List
     * @throws SchedulerException 异常
     */
    public List<Trigger> queryTriggerListByJobName(String jobName) throws SchedulerException {
        if (scheduler == null) {
            return new ArrayList<Trigger>();
        }
        JobKey key = new JobKey(jobName, SCHEDULER_GROUP);
        return (List<Trigger>) scheduler.getTriggersOfJob(key);
    }
    /**
     * 根据任务名称获取触发器
     *
     * @param jobName 任务名
     * @return List
     * @throws SchedulerException 异常
     */
    public List<TriggerObject> queryTriggerListByJobName(String jobName,Map<String,Trigger.TriggerState> stateMap) throws SchedulerException {
        if (scheduler == null) {
            return new ArrayList<TriggerObject>();
        }
        JobKey key = new JobKey(jobName, SCHEDULER_GROUP);
        List<Trigger> listTrigger = (List<Trigger>) scheduler.getTriggersOfJob(key);
        List<TriggerObject> list = new LinkedList<>();
        if(null!=listTrigger && listTrigger.size()>0){
            for(Trigger t:listTrigger){
                TriggerObject to = new TriggerObject();
                to.setJobName(jobName);
                String triggerName = t.getKey().getName();
                to.setName(triggerName);
                if(null!=stateMap){
                    if(null!=stateMap.get(triggerName)){
                        to.setState(stateMap.get(triggerName).name());
                    }
                }
                to.setDescription(t.getDescription());
                list.add(to);
            }
        }
        return list;
    }
    /**
     * 取得触发器的状态
     *
     * @param list 触发器集合
     * @return map
     * @throws SchedulerException 异常
     */
    public HashMap<String, Trigger.TriggerState> getTriggerStatus(List<Trigger> list) throws SchedulerException {
        if (scheduler == null) {
            return new HashMap<String, Trigger.TriggerState>();
        }
        HashMap<String, Trigger.TriggerState> map = new HashMap<String, Trigger.TriggerState>();
        for (Iterator<Trigger> it = list.iterator(); it.hasNext(); ) {
            Trigger trigger = it.next();
            TriggerKey key = trigger.getKey();
            Trigger.TriggerState state = scheduler.getTriggerState(key);
            map.put(key.getName(), state);
        }
        return map;
    }

    /**
     * 判断计划是否存在
     *
     * @param trigName 触发器名称
     * @return boolean
     * @throws SchedulerException 异常
     */
    public boolean isTriggerExists(String trigName) throws SchedulerException {
        if (scheduler == null) {
            return false;
        }
        TriggerKey triggerKey = new TriggerKey(trigName, SCHEDULER_GROUP);
        return scheduler.checkExists(triggerKey);
    }

    /**
     * 添加计划
     *
     * @param jobName  任务名
     * @param trigName 计划名
     * @param planJson 计划详细
     * @throws SchedulerException 异常
     */
    public void saveTrigger(String jobName, String trigName, String planJson) throws SchedulerException, ParseException {
        if (scheduler == null) {
            return;
        }
        JobKey jobKey = new JobKey(jobName, SCHEDULER_GROUP);
        TriggerBuilder<Trigger> tb = TriggerBuilder.newTrigger();
        tb.withIdentity(trigName, SCHEDULER_GROUP);
        //tb.withDescription(description);
        setTrigBuilder(planJson, tb);
        tb.forJob(jobKey);
        Trigger trig = tb.build();
        scheduler.scheduleJob(trig);
    }

    /**
     * 添加触发器
     *
     * @param jobName 任务名
     * @param trigName 触发器名
     * @param minute 分钟值
     * @throws SchedulerException 异常
     */
    public void saveTrigger(String jobName, String trigName, int minute) throws SchedulerException {
        if (scheduler == null) {
            return;
        }
        JobKey jobKey = new JobKey(jobName, SCHEDULER_GROUP);
        TriggerBuilder<Trigger> tb = TriggerBuilder.newTrigger();
        tb.withIdentity(trigName, SCHEDULER_GROUP);
        ScheduleBuilder sb = CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInMinutes(minute);
        tb.startNow();
        tb.withSchedule(sb);
        tb.withDescription("每:" + minute + "分钟执行!");

        tb.forJob(jobKey);
        Trigger trig = tb.build();
        scheduler.scheduleJob(trig);
    }

    private void setTrigBuilder(String planJson, TriggerBuilder<Trigger> tb) throws ParseException {
        planJson = StringEscapeUtils.unescapeHtml4(planJson);
        JSONObject jsonObject = JSONObject.fromObject(planJson);

        PlanObject planObject = (PlanObject) JSONObject.toBean(jsonObject, PlanObject.class);
        int type = planObject.getType();
        String value = planObject.getTimeInterval();
        DateFormat dateFormat = DateFormat.getDateTimeInstance();

        switch (type) {
            /*
            启动一次
             */
            case 1:
                Date date = TimeUtil.convertString(value);
                tb.startAt(date);
                tb.withDescription("执行一次,执行时间:" + dateFormat.format(date));
                break;
            /*
            每分钟执行
             */
            case 2:
                int minute = Integer.parseInt(value);
                ScheduleBuilder sb = CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInMinutes(minute);

                tb.startNow();
                tb.withSchedule(sb);
                tb.withDescription("每:" + minute + "分钟执行!");
                break;
            /*
            每天时间点执行
             */
            case 3:
                String[] aryTime = value.split(":");
                int hour = Integer.parseInt(aryTime[0]);
                int m = Integer.parseInt(aryTime[1]);
                ScheduleBuilder sb1 = CronScheduleBuilder.dailyAtHourAndMinute(hour, m);
                tb.startNow();
                tb.withSchedule(sb1);
                tb.withDescription("每天：" + hour + ":" + m + "执行!");
                break;
            /*
            每周时间点执行
             */
            case 4:
                //0 15 10 ? * MON-FRI Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday
                String[] aryExpression = value.split("[|]");
                String week = aryExpression[0];
                String[] aryTime1 = aryExpression[1].split(":");
                String h1 = aryTime1[0];
                String m1 = aryTime1[1];
                String cronExperssion = "0 " + m1 + " " + h1 + " ? * " + week;
                ScheduleBuilder sb4 = CronScheduleBuilder.cronSchedule(cronExperssion);
                tb.startNow();
                tb.withSchedule(sb4);
                String weekName = getWeek(week);
                tb.withDescription("每周：" + weekName + "," + h1 + ":" + m1 + "执行!");
                break;
            //每月执行
            case 5:
                //0 15 10 15 * ?
                String[] aryExpression5 = value.split("[|]");
                String day = aryExpression5[0];
                String[] aryTime2 = aryExpression5[1].split(":");
                String h2 = aryTime2[0];
                String m2 = aryTime2[1];
                String cronExperssion1 = "0 " + m2 + " " + h2 + " " + day + " * ?";
                ScheduleBuilder sb5 = CronScheduleBuilder.cronSchedule(cronExperssion1);
                tb.startNow();
                tb.withSchedule(sb5);
                String dayName = getDay(day);
                tb.withDescription("每月:" + dayName + "," + h2 + ":" + m2 + "执行!");
                break;
            /*
            表达式
             */
            case 6:
                ScheduleBuilder sb6 = CronScheduleBuilder.cronSchedule(value);
                tb.startNow();
                tb.withSchedule(sb6);
                tb.withDescription("CronTrigger表达式:" + value);
                break;
        }
    }

    private String getDay(String day) {
        String[] aryDay = day.split(StringPool.COMMA);
        int len = aryDay.length;
        String str = "";
        for (int i = 0; i < len; i++) {
            String tmp = aryDay[i];
            tmp = "L".equals(tmp) ? "最后一天" : tmp;
            if (i < len - 1) {
                str += tmp + ",";
            } else {
                str += tmp;
            }
        }
        return str;
    }

    /**
     * 取得星期名称
     *
     * @param week 星期
     * @return 星期名称
     */
    private String getWeek(String week) {
        String[] aryWeek = week.split(",");
        int len = aryWeek.length;
        String str = "";
        for (int i = 0; i < len; i++) {
            if (i < len - 1) {
                str += mapWeek.get(aryWeek[i]) + ",";
            } else {
                str += mapWeek.get(aryWeek[i]);
            }
        }
        return str;
    }


    /**
     * 设置任务参数
     *
     * @param json 封装参数
     * @param jb JobBuilder实例
     */
    private void setJobMap(String json, JobBuilder jb) {
        JSONArray aryJson = JSONArray.fromObject(json);
        ParameterObj[] list = (ParameterObj[]) JSONArray.toArray(aryJson, ParameterObj.class);
        for (int i = 0; i < list.length; i++) {
            ParameterObj obj = (ParameterObj) list[0];
            String type = obj.getType();
            String name = obj.getName();
            String value = obj.getValue();
            if ("int".equals(type)) {
                if (StringUtil.isEmpty(value)) {
                    jb.usingJobData(name, 0);
                } else {
                    jb.usingJobData(name, Integer.parseInt(value));
                }
            } else if ("long".equals(type)) {
                if (StringUtil.isEmpty(value)) {
                    jb.usingJobData(name, 0);
                } else {
                    jb.usingJobData(name, Long.parseLong(value));
                }
            } else if ("float".equals(type)) {
                if (StringUtil.isEmpty(value)) {
                    jb.usingJobData(name, 0.0);
                } else {
                    jb.usingJobData(name, Float.parseFloat(value));
                }
            } else if ("boolean".equals(type)) {
                if (StringUtil.isEmpty(value)) {
                    jb.usingJobData(name, false);
                } else {
                    jb.usingJobData(name, Boolean.parseBoolean(value));
                }
            } else {
                jb.usingJobData(name, value);
            }
        }
    }

    /**
     * 删除任务
     *
     * @param jobName 任务名
     * @throws SchedulerException 异常
     * @throws ClassNotFoundException 异常
     */
    public void delJob(String jobName) throws SchedulerException {
        if (scheduler == null) {
            return;
        }
        JobKey key = new JobKey(jobName, SCHEDULER_GROUP);
        scheduler.deleteJob(key);
    }

    public Trigger getTrigger(String triggerName) throws SchedulerException {
        if (scheduler == null) {
            return null;
        }
        TriggerKey key = new TriggerKey(triggerName, SCHEDULER_GROUP);
        return scheduler.getTrigger(key);
    }

    /**
     * 删除计划
     *
     * @param triggerName 计划名（触发器名）
     * @throws SchedulerException 异常
     * @throws ClassNotFoundException 异常
     */
    public void delTrigger(String triggerName) throws SchedulerException {
        if (scheduler == null) {
            return;
        }
        TriggerKey key = new TriggerKey(triggerName, SCHEDULER_GROUP);
        scheduler.unscheduleJob(key);
    }

    /**
     * 停止或暂停触发器
     *
     * @param triggerName 计划名（触发器名）
     * @throws SchedulerException 异常
     */
    public void toggleTriggerRun(String triggerName) throws SchedulerException {
        if (scheduler == null) {
            return;
        }
        TriggerKey key = new TriggerKey(triggerName, SCHEDULER_GROUP);
        Trigger.TriggerState state = scheduler.getTriggerState(key);
        if (state == Trigger.TriggerState.PAUSED) {
            scheduler.resumeTrigger(key);
        } else if (state == Trigger.TriggerState.NORMAL) {
            scheduler.pauseTrigger(key);
        }
    }


    /**
     * 直接执行任务
     *
     * @param jobName 任务名
     * @throws SchedulerException 异常
     */
    public void executeJob(String jobName) throws SchedulerException {
        if (scheduler == null) {
            return;
        }
        JobKey key = new JobKey(jobName, SCHEDULER_GROUP);
        scheduler.triggerJob(key);
    }

    /**
     * 启动
     * @throws SchedulerException 异常
     */
    public void start() throws SchedulerException {
        scheduler.start();
    }

    /**
     * 关闭
     * @throws SchedulerException 异常
     */
    public void shutdown() throws SchedulerException {
        scheduler.standby();
    }

    /**
     * 是否启动
     * @return boolean
     * @throws SchedulerException 异常
     */
    public boolean isStarted() throws SchedulerException {
        return scheduler.isStarted();
    }

    /**
     * 是否挂起
     * @return boolean
     * @throws SchedulerException 异常
     */
    public boolean isInStandbyMode() throws SchedulerException {
        ISysJobLogService ii = AppUtil.getBean(ISysJobLogService.class);
        return scheduler.isInStandbyMode();
    }
}